# H2DatabaseSettings.java

    package org.codefilarete.stalactite.sql.h2;

    import java.util.Collections;

    import org.codefilarete.stalactite.engine.DatabaseVendorSettings;
    import org.codefilarete.stalactite.engine.SQLOperationsFactories;
    import org.codefilarete.stalactite.engine.SQLOperationsFactoriesBuilder;
    import org.codefilarete.stalactite.mapping.id.sequence.DatabaseSequenceSelector;
    import org.codefilarete.stalactite.sql.ConnectionProvider;
    import org.codefilarete.stalactite.sql.DMLNameProviderFactory;
    import org.codefilarete.stalactite.sql.DatabaseSequenceSelectorFactory;
    import org.codefilarete.stalactite.sql.GeneratedKeysReaderFactory.DefaultGeneratedKeysReaderFactory;
    import org.codefilarete.stalactite.sql.h2.H2DialectResolver.H2DatabaseSignet;
    import org.codefilarete.stalactite.sql.ddl.DDLSequenceGenerator;
    import org.codefilarete.stalactite.sql.h2.ddl.H2DDLTableGenerator;
    import org.codefilarete.stalactite.sql.ddl.SqlTypeRegistry;
    import org.codefilarete.stalactite.sql.ddl.structure.Column;
    import org.codefilarete.stalactite.sql.statement.DMLGenerator;
    import org.codefilarete.stalactite.sql.statement.ReadOperationFactory;
    import org.codefilarete.stalactite.sql.statement.WriteOperationFactory;
    import org.codefilarete.stalactite.sql.h2.statement.binder.H2ParameterBinderRegistry;
    import org.codefilarete.stalactite.sql.h2.statement.binder.H2TypeMapping;
    import org.codefilarete.stalactite.sql.statement.binder.ParameterBinder;
    import org.codefilarete.stalactite.sql.statement.binder.ParameterBinderIndex;
    import org.codefilarete.tool.VisibleForTesting;
    import org.codefilarete.tool.collection.CaseInsensitiveSet;

    /**
     * @author Guillaume Mary
     */
    public class H2DatabaseSettings extends DatabaseVendorSettings {

    	/**
    	 * Derby keywords, took from <a href="https://www.h2database.com/html/advanced.html#keywords">H2 documentation</a> because those of
    	 * it JDBC Drivers are not enough / accurate. Moreover it might requires a Database Connection to retrieve them (see {@link org.h2.jdbc.JdbcDatabaseMetaData#getSQLKeywords()})
    	 */
    	@VisibleForTesting
    	static final String[] KEYWORDS = new String[] {
    			"ALL", "AND", "ANY", "ARRAY", "AS", "ASYMMETRIC", "AUTHORIZATION",
    			"BETWEEN", "BOTH",
    			"CASE", "CAST", "CHECK", "CONSTRAINT", "CROSS", "CURRENT_CATALOG", "CURRENT_DATE", "CURRENT_PATH", "CURRENT_ROLE", "CURRENT_SCHEMA",
    				"CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER",
    			"DAY", "DEFAULT", "DISTINCT",
    			"ELSE", "END", "EXCEPT", "EXISTS",
    			"FALSE", "FETCH", "FOR", "FOREIGN", "FROM", "FULL",
    			"GROUP", "GROUPS", "HAVING", "HOUR",
    			"IF", "ILIKE", "IN", "INNER", "INTERSECT", "INTERVAL", "IS",
    			"JOIN",
    			"KEY",
    			"LEADING", "LEFT", "LIKE", "LIMIT", "LOCALTIME", "LOCALTIMESTAMP",
    			"MINUS", "MINUTE", "MONTH",
    			"NATURAL", "NOT", "NULL",
    			"OFFSET", "ON", "OR", "ORDER", "OVER",
    			"PARTITION", "PRIMARY",
    			"QUALIFY",
    			"RANGE", "REGEXP", "RIGHT", "ROW", "ROWNUM", "ROWS",
    			"SECOND", "SELECT", "SESSION_USER", "SET", "SOME", "SYMMETRIC", "SYSTEM_USER",
    			"TABLE", "TO", "TOP", "TRAILING", "TRUE",
    			"UESCAPE", "UNION", "UNIQUE", "UNKNOWN", "USER", "USING",
    			"VALUE", "VALUES",
    			"WHEN", "WHERE", "WINDOW", "WITH",
    			"YEAR",
    			"_ROWID_"
    	};

    	public static final H2DatabaseSettings H2_1_4 = new H2DatabaseSettings();

    	private H2DatabaseSettings() {
    		this(new H2SQLOperationsFactoriesBuilder(), new H2ParameterBinderRegistry());
    	}

    	private H2DatabaseSettings(H2SQLOperationsFactoriesBuilder h2SQLOperationsFactoriesBuilder, H2ParameterBinderRegistry parameterBinderRegistry) {
    		super(new H2DatabaseSignet(1, 4),
    				Collections.unmodifiableSet(new CaseInsensitiveSet(KEYWORDS)),
    				'"',
    				new H2TypeMapping(),
    				parameterBinderRegistry,
    				h2SQLOperationsFactoriesBuilder,
    				new DefaultGeneratedKeysReaderFactory(parameterBinderRegistry),
    				1000,
    				true);
    	}

    	private static class H2SQLOperationsFactoriesBuilder implements SQLOperationsFactoriesBuilder {

    		private final ReadOperationFactory readOperationFactory;
    		private final WriteOperationFactory writeOperationFactory;

    		private H2SQLOperationsFactoriesBuilder() {
    			this.readOperationFactory = new ReadOperationFactory();
    			this.writeOperationFactory = new WriteOperationFactory();
    		}

    		private ReadOperationFactory getReadOperationFactory() {
    			return readOperationFactory;
    		}

    		private WriteOperationFactory getWriteOperationFactory() {
    			return writeOperationFactory;
    		}

    		@Override
    		public SQLOperationsFactories build(ParameterBinderIndex<Column, ParameterBinder> parameterBinders, DMLNameProviderFactory dmlNameProviderFactory, SqlTypeRegistry sqlTypeRegistry) {
    			DMLGenerator dmlGenerator = new DMLGenerator(parameterBinders, DMLGenerator.NoopSorter.INSTANCE, dmlNameProviderFactory);
    			H2DDLTableGenerator ddlTableGenerator = new H2DDLTableGenerator(sqlTypeRegistry, dmlNameProviderFactory);
    			DDLSequenceGenerator ddlSequenceGenerator = new DDLSequenceGenerator(dmlNameProviderFactory);
    			return new SQLOperationsFactories(writeOperationFactory, readOperationFactory, dmlGenerator, ddlTableGenerator, ddlSequenceGenerator, new H2SequenceSelectorFactory(readOperationFactory));
    		}
    	}

    	private static class H2SequenceSelectorFactory implements DatabaseSequenceSelectorFactory {

    		private final ReadOperationFactory readOperationFactory;

    		private H2SequenceSelectorFactory(ReadOperationFactory readOperationFactory) {
    			this.readOperationFactory = readOperationFactory;
    		}

    		@Override
    		public DatabaseSequenceSelector create(org.codefilarete.stalactite.sql.ddl.structure.Sequence databaseSequence, ConnectionProvider connectionProvider) {
    			return new DatabaseSequenceSelector(databaseSequence, "select next value for " + databaseSequence.getAbsoluteName(), readOperationFactory, connectionProvider);
    		}
    	}
    }